﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using ESpace;
using System.IO;
using ElementClasses;

namespace Organizer
{

    public partial class FormMess : Form
    {
        DIR actDir;
        bool bEdit = false;
        bool bOk = false;

        // возвращаемые данные
        public string ret_Name;
        public Mess ret_Element;

        public FormMess()
        {
            InitializeComponent();
        }

        public bool ShowDialog(DIR actDir, Element EditElementForCopy)
        {
            bOk = false;
            this.actDir = actDir;
            bEdit = EditElementForCopy != null;

            numHour.Value = 0;
            numMin.Value = 0;
            numPYear.Value = 0;
            numPMonth.Value = 0;
            numPDay.Value = 0;
            numPHour.Value = 0;
            numPMin.Value = 0;

            if (bEdit)
            {
                Mess mes = (Mess)EditElementForCopy;
                txbName.Text = mes.Name;

                chbxDelAfterExecute.Checked = mes.DelAfterExecute;

                txbText.Text = mes.Text;
                ddlPeriod.SelectedIndex = 0;

                cldDate.Value = mes.Dt;
                if (mes.Dt.TimeOfDay != TimeSpan.Zero)
                {
                    chbxTime.Checked = true;
                    numHour.Value = mes.Dt.TimeOfDay.Hours;
                    numMin.Value = mes.Dt.TimeOfDay.Minutes;
                }
                else
                {
                    chbxTime.Checked = false;
                }
                chbxActive.Checked = mes.IsActive;

                // определение периода
                if (mes.IsPeriod)
                {
                    if (mes.PYear == 1 && mes.PMonth == 0 && mes.PDay == 0 && mes.PHour == 0 && mes.PMin == 0) ddlPeriod.SelectedIndex = 1;
                    else if (mes.PYear == 0 && mes.PMonth == 1 && mes.PDay == 0 && mes.PHour == 0 && mes.PMin == 0) ddlPeriod.SelectedIndex = 2;
                    else if (mes.PYear == 0 && mes.PMonth == 0 && mes.PDay == 1 && mes.PHour == 0 && mes.PMin == 0) ddlPeriod.SelectedIndex = 3;
                    else if (mes.PYear == 0 && mes.PMonth == 0 && mes.PDay == 0 && mes.PHour == 1 && mes.PMin == 0) ddlPeriod.SelectedIndex = 4;
                    else
                    {
                        ddlPeriod.SelectedIndex = 5;
                        numPYear.Value = mes.PYear;
                        numPMonth.Value = mes.PMonth;
                        numPDay.Value = mes.PDay;
                        numPHour.Value = mes.PHour;
                        numPMin.Value = mes.PMin;
                    }
                }

            }
            else
            {
                txbText.Text = "";
                ddlPeriod.SelectedIndex = 0;
                cldDate.Value = DateTime.Today;
                chbxTime.Checked = false;
                chbxActive.Checked = true;
                chbxDelAfterExecute.Checked = true;

                // смотрим название по умолчанию
                txbName.Text = actDir.GetUnicName("Новое сообщение", "mess");
            }

            txbName.Enabled = !bEdit;

            ShowDialog();
            return bOk;
        }

        private void btnOk_Click(object sender, EventArgs e)
        {
            // проверка имени
            string nm = txbName.Text.Trim();
            if (!bEdit)
            {
                string err = Gl.CheckName(actDir, null, nm);
                if (err != "")
                {
                    Gl.ErrMess(err);
                    txbName.Focus();
                    txbName.SelectAll();
                    return;
                }
            }

            DateTime dt = cldDate.Value.Date; if (chbxTime.Checked) dt = dt.AddHours((int)numHour.Value).AddMinutes((int)numMin.Value);
            int[] period = new int[5];
            switch (ddlPeriod.SelectedIndex)
            {
                case 1: period[0] = 1; break; // год
                case 2: period[1] = 1; break; // месяц
                case 3: period[2] = 1; break; // день
                case 4: period[3] = 1; break; // час
                case 5:                       // период
                    {
                        period[0] = (int)numPYear.Value;
                        period[1] = (int)numPMonth.Value;
                        period[2] = (int)numPDay.Value;
                        period[3] = (int)numPHour.Value;
                        period[4] = (int)numPMin.Value;

                        // проверка правильности периода
                        bool bNotPeriod = true;
                        for (int i = 0; i < 5; i++) if (period[i] != 0) { bNotPeriod = false; break; }
                        if (bNotPeriod) { Gl.ErrMess("Выбрано повторение сообщения, но не указан период"); numPYear.Focus(); return; }

                        break;
                    }
            }

            Mess retMess = new Mess(nm, actDir, NetStatus.Local, txbText.Text, dt, period);
            retMess.IsActive = chbxActive.Checked;
            retMess.DelAfterExecute = chbxDelAfterExecute.Checked && !retMess.IsPeriod;

            ret_Name = nm;
            ret_Element = retMess;

            bOk = true;
            Close();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            bOk = false;
            Close();
        }

        private void chbxTime_CheckedChanged(object sender, EventArgs e)
        {
            grTime.Enabled = chbxTime.Checked;
        }

        private void ddlPeriod_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ddlPeriod.SelectedIndex > 0)
            {
                chbxDelAfterExecute.Checked = false;
                chbxDelAfterExecute.Enabled = false;
                lblPeriod.ForeColor = SystemColors.ControlText;
            }
            else
            {
                lblPeriod.ForeColor = SystemColors.GrayText;
                chbxDelAfterExecute.Enabled = true;
            }

            if (ddlPeriod.SelectedIndex == 5)
            {
                grPeriod.Visible = true;
                Height = 288;
            }
            else
            {
                grPeriod.Visible = false;
                Height = 233;
            }
        }

    }

    public class MessUnit : VisibleUnit
    {
        public override string Ext { get { return "mess"; } }
        public override string MenuName { get { return "cообщение"; } }

        // настройки
        int SecondBeforFirstMessage { get { return (int)SettEl[0].Value; } }

        DateTime dtFirstShowMessage;

        public MessUnit(API_Funtions ExtFunc) : base(ExtFunc) 
        {
            // по умолчанию 5 минут. Даем немного времени для подключения к инету
            SettEl.Add(new SettingsElement("Секунд до показа первого сообщения", "SecondBeforFirstMessage", 300, SettingsElType.Int));  
        }

        public override Element LoadElement(ESpace.EStream st, ESpace.DBItem Parent) { return new Mess(st, Parent); }

        public override Element CreateElementBySourceDate(string Name, ESpace.DBItem Parent, string SourceData)
        {
            Mess ret = new Mess(Name, Parent, NetStatus.Local, "", DateTime.MinValue);
            ret.SourceData = SourceData;
            return ret;
        }

        protected override List<Bitmap> GetIcoList()
        {
            List<Bitmap> bmpL = new List<Bitmap>();
            bmpL.Add(Properties.Resources.evt);
            bmpL.Add(Properties.Resources.evt_L);
            bmpL.Add(Properties.Resources.evt_ok);
            bmpL.Add(Properties.Resources.evt_U);
            bmpL.Add(Properties.Resources.evt_notAct);
            bmpL.Add(Properties.Resources.evt_notAct_L);
            bmpL.Add(Properties.Resources.evt_notAct_ok);
            bmpL.Add(Properties.Resources.evt_notAct_U);
            bmpL.Add(Properties.Resources.period);
            bmpL.Add(Properties.Resources.period_L);
            bmpL.Add(Properties.Resources.period_ok);
            bmpL.Add(Properties.Resources.period_U);
            bmpL.Add(Properties.Resources.period_notAct);
            bmpL.Add(Properties.Resources.period_notAct_L);
            bmpL.Add(Properties.Resources.period_notAct_ok);
            bmpL.Add(Properties.Resources.period_notAct_U);
            return bmpL;
        }

        public override int GetNumIco(Element el)
        {
            Mess mes = (Mess)el;

            int ret = -1;
            switch (mes.NetStat)
            {
                case NetStatus.Local: ret = 0; break;
                case NetStatus.NotActual: ret = 1; break;
                case NetStatus.Write: ret = 3; break;
                //case NetStatus.Actual: ret = Gl.connState == Gl.ConnectState.OnLine ? 2 : 1; break;
                case NetStatus.Actual: ret = Gl.IsActualFlag ? 2 : 1; break;

            }
            if (ret == -1) return -1;

            if (mes.IsPeriod) ret += 8;
            if (!mes.IsActive) ret += 4;
            return icoIndL[ret];
        }

        #region *** Внешние события **************************************************

        /// <summary> событие возникает после загрузки базы. Сам юнит создается до загрузки базы
        /// событие используется для выполнения начальных дейтсвий с уже загруженой базой </summary>
        public override void EndCreate() 
        {
            dtFirstShowMessage = DateTime.Now.AddSeconds(SecondBeforFirstMessage);
        }

        /// <summary> Событие запроса на создание нового элемента пользователем </summary>
        public override Element CreateNew(DIR Parent, NetStatus NetStat)
        {
            Mess ret = null;
            FormMess frmEdit = new FormMess();
            if (frmEdit.ShowDialog(Parent, null))
            {
                ret = new Mess(frmEdit.ret_Name, Parent, NetStat, "", DateTime.MinValue);
                ret.SourceData = frmEdit.ret_Element.SourceData;
                ret.NetStat = NetStat;
            }
            return ret;
        }

        /// <summary> Событие запроса на редактирование элемента пользователем </summary>
        public override bool Edit(Element EditEl)
        {
            EditEl.IsEdit = true;
            bool ret = false;
            DIR editParent = (DIR)EditEl.Parent;
            Mess editEl = (Mess)EditEl;
            FormMess frmEdit = new FormMess();
            if (frmEdit.ShowDialog(editParent, EditEl))
            {
                editEl.SourceData = frmEdit.ret_Element.SourceData;
                editEl.DtRemindAgain = DateTime.MinValue; // сброс повторения вновь, так как редактировалось
                editEl.NetStat = EditEl.NetStat == NetStatus.Local ? NetStatus.Local : NetStatus.NotActual;
                ret = true;
                API_Func.UpdateIcoInDirIfShow((DIR)editEl.Parent);
                API_Func.UpdateComments((DIR)editEl.Parent);
            }
            editParent = null;
            editEl = null;
            EditEl.IsEdit = false;
            return ret;
        }

        /// <summary> Событие основного таймера </summary>
        public override void TimerTick(DateTime tmrDt, DIR DB)
        {
            // если время выводить сообщения еще не подошло, выходим
            if (tmrDt < dtFirstShowMessage) return;

            List<DBItem> el = DB.GetAllRecords(new string[1] { Ext }, new string[1] { Gl.GetSystemDir(Gl.SystemDir.Recycler).FullPath });
            if (!Gl.sett.Guests)
            {
                // находжение сообщений, чье время подошло
                for (int i = 0; i < el.Count; i++)
                {
                    Mess mes = (Mess)el[i];
                    if (mes.NearDt <= DateTime.Now && mes.IsActive) //&& !mes.RemindIsShow
                    {
                        //mes.RemindIsShow = true;
                        FormShowMess frmMessage = new FormShowMess();
                        frmMessage.ShowDialog(mes);
                        CloseRemind(mes, frmMessage.ret_timeRemindAgain);
                        frmMessage.Dispose();
                    }
                }
            }
        }

        /// <summary> событие изменения режима гости </summary>
        public override void GuestModeChange(bool bGuest) { ;}

        #endregion *** Внешние события **************************************************

        // событие закрытия формы напоминания события
        void CloseRemind(Mess mes, int timeRemindAgain)
        {
            DIR Dir = (DIR)mes.Parent;

            // если перенос на попозже
            if (timeRemindAgain > 0)
            {
                DateTime dt = DateTime.Now;
                //mes.DtRemindAgain = dt.AddSeconds(-dt.Second).AddMinutes(timeRemindAgain);
                mes.DtRemindAgain = dt.AddMinutes(timeRemindAgain);
            }
            else
            {
                mes.DtRemindAgain = DateTime.MinValue;
                // если сообщение разовое, снимаем активность
                if (!mes.IsPeriod)
                {
                    mes.IsActive = false;
                }
                // если событие периодическое
                else
                {
                    // если сообщение повторяемое
                    if (mes.IsPeriod) mes.SetNextPeriodDate();
                }

                // если сообщение разовое и его необходимо удалить после выполнения, удаляем
                if (mes.DelAfterExecute)
                {
                    API_Func.DeleteElement(mes);
                    API_Func.RefillDirIfShow(Dir);
                }
            }

            if (mes.NetStat != NetStatus.Local) mes.NetStat = NetStatus.NotActual;
            API_Func.SaveData();
            API_Func.UpdateIcoInDirIfShow(Dir);
            API_Func.UpdateComments(Dir);
        }

    }

    public class Mess : Element
    {
        int ActVer = 0; // версия сохранения

        public override string Ext { get { return "mess"; } }

        #region *** данные в БД ***************

        public string Text = "";

        public DateTime Dt = DateTime.MinValue; // стартовая дата

        public int PYear = 0; // период лет
        public int PMonth = 0; // период месяцев
        public int PDay = 0; // период дней
        public int PHour = 0; // период часов
        public int PMin = 0; // период минут

        public bool IsActive = true;

        public bool DelAfterExecute = true;

        #endregion *** данные в БД ************

        public override string SourceData
        {
            get
            {
                string ret = "";
                using (MemoryStream ms = new MemoryStream())
                {
                    using (BinaryWriter bw = new BinaryWriter(ms))
                    {
                        bw.Write(ActVer);// задел на версионность
                        bw.Write(Text);
                        bw.Write(Dt.Ticks);
                        bw.Write((Int16)PYear);
                        bw.Write((byte)PMonth);
                        bw.Write((byte)PDay);
                        bw.Write((byte)PHour);
                        bw.Write((byte)PMin);
                        bw.Write(IsActive);
                        bw.Write(DelAfterExecute);
                        ret = Convert.ToBase64String(ms.ToArray());
                    }
                }
                return ret;
            }
            set
            {
                using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(value)))
                {
                    using (BinaryReader br = new BinaryReader(ms))
                    {
                        br.ReadInt32(); // задел на версионность
                        Text = br.ReadString();
                        Dt = new DateTime(br.ReadInt64());
                        PYear = br.ReadInt16();
                        PMonth = br.ReadByte();
                        PDay = br.ReadByte();
                        PHour = br.ReadByte();
                        PMin = br.ReadByte();
                        IsActive = br.ReadBoolean();
                        DelAfterExecute = br.ReadBoolean();
                    }
                }

            }
        }

        // ближайщая дата вывода напоминания (стартовая дата или отложеная дата)
        public DateTime NearDt
        {
            get
            {
                if (DtRemindAgain != DateTime.MinValue) return DtRemindAgain;
                return Dt;
            }
        }

        public bool IsPeriod { get { return PYear > 0 || PMonth > 0 || PDay > 0 || PHour > 0 || PMin > 0; } }

        //public bool RemindIsShow = false; // открыто окно напоминания (параметр не сохраняется)
        public DateTime DtRemindAgain = DateTime.MinValue; // дата, когда нужно напомнить снова (вне периода). Если максимальное, значит не нужно напоминать снова

        public Mess(EStream st, DBItem Parent)
            : base(st, Parent)
        {
            st.LoadInt(); // задел на версионность

            Text = st.LoadStr();
            Dt = st.LoadDate();
            PYear = st.LoadInt(); // период лет
            PMonth = st.LoadInt(); // период месяцев
            PDay = st.LoadInt(); // период дней
            PHour = st.LoadInt(); // период часов
            PMin = st.LoadInt(); // период минут
            IsActive = st.LoadBool();
            DelAfterExecute = st.LoadBool();
        }

        /// <summary> Конструктор для разового сообщения </summary>
        public Mess(string Name, DBItem Parent, NetStatus NetStat, string Text, DateTime Dt)
            : base(Name, false, Parent, NetStat)
        {
            this.Text = Text;
            this.Dt = Dt;
        }

        public override void Dispose() { ; }

        /// <summary> Конструктор для периодического сообщения </summary>
        public Mess(string Name, DBItem Parent, NetStatus NetStat, string Text, DateTime Dt, int[] period)
            : base(Name, false, Parent, NetStat)
        {
            this.Text = Text;
            this.Dt = Dt;
            PYear = period[0]; // период лет
            PMonth = period[1]; // период месяцев
            PDay = period[2]; // период дней
            PHour = period[3]; // период часов
            PMin = period[4]; // период минут
        }

        public override void Save(EStream st)
        {
            base.Save(st);

            st.SaveInt(ActVer); // задел на версионность

            st.SaveStr(Text);
            st.SaveDate(Dt);
            st.SaveInt(PYear); // период лет
            st.SaveInt(PMonth); // период месяцев
            st.SaveInt(PDay); // период дней
            st.SaveInt(PHour); // период часов
            st.SaveInt(PMin); // период минут
            st.SaveBool(IsActive);
            st.SaveBool(DelAfterExecute);
        }

        public override TrySynchronizeResult TrySynchronize(Element el)
        {
            Mess mes = (Mess)el;
            // если сообщение разовое
            if (!IsPeriod || !mes.IsPeriod) return TrySynchronizeResult.SourceDataCanBeReplaced;

            // если отличаются данные
            if (PYear != mes.PYear) return TrySynchronizeResult.SourceDataCanBeReplaced; // период лет
            if (PMonth != mes.PMonth) return TrySynchronizeResult.SourceDataCanBeReplaced; // период месяцев
            if (PDay != mes.PDay) return TrySynchronizeResult.SourceDataCanBeReplaced; // период дней
            if (PHour != mes.PHour) return TrySynchronizeResult.SourceDataCanBeReplaced; // период часов
            if (PMin != mes.PMin) return TrySynchronizeResult.SourceDataCanBeReplaced; // период минут
            if (IsActive != mes.IsActive) return TrySynchronizeResult.SourceDataCanBeReplaced;
            if (DelAfterExecute != mes.DelAfterExecute) return TrySynchronizeResult.SourceDataCanBeReplaced;

            #region *** смотрим дату. если она кратна, то можно просто дописать новую ***
            DateTime dt1 = Dt; // меньшая дата
            DateTime dt2 = mes.Dt; // бОльшая дата

            if (dt1 == dt2) return TrySynchronizeResult.SourceDataCanBeReplaced;

            if (dt1 > dt2) { dt1 = mes.Dt; dt2 = Dt; } // если старая дата больше новой
            // пробуем найти кратность
            while (dt1 < dt2)
            {
                dt1 = dt1.AddYears(PYear).AddMonths(PMonth).AddDays(PDay).AddHours(PHour).AddMinutes(PMin);
                // если кратность найдена, пишем в актуальную бОльшую дату
                if (dt1 == dt2)
                {
                    Dt = dt2;
                    return TrySynchronizeResult.NeedReWrite;
                }
            }
            #endregion *** смотрим дату. если она кратна, то можно просто дописать новую ***

            return TrySynchronizeResult.SourceDataCanBeReplaced;
        }

        public void SetNextPeriodDate()
        {
            if (!IsPeriod) throw new Exception("Попытка изменения даты у НЕпериодического сообщения");
            while (Dt < DateTime.Now)
                Dt = Dt.AddYears(PYear).AddMonths(PMonth).AddDays(PDay).AddHours(PHour).AddMinutes(PMin);
        }

        public override string GetComment()
        {
            return NearDt.ToString("dd.MM.yy HH:mm:ss");
        }

        public override void TimerTick(DateTime tmrDt, DIR DB) { ;}

    }

}
